Skip to content

feat: AI-assisted CSV transaction import (human-in-the-loop)#17

Open
toolpathguy wants to merge 1 commit into
mainfrom
feat/ai-csv-import
Open

feat: AI-assisted CSV transaction import (human-in-the-loop)#17
toolpathguy wants to merge 1 commit into
mainfrom
feat/ai-csv-import

Conversation

@toolpathguy

Copy link
Copy Markdown
Owner

Fixes #9

Upload a bank/credit-card CSV and have the AI map an arbitrary layout to normalized transactions. The AI proposes rows; the user reviews/edits them in a staging table and approves per-row; only approved rows are committed via the existing direct journal writer. Builds on the merged #8 AI plumbing (shared Anthropic client + Settings-page key) — no new dependency, no config changes.

How it works

pages/import.vue → useImport → /api/import/parse  (structured output, NEVER writes)
                             → /api/import/commit (the ONLY write path, approved rows only)
  • Parse (parse.post.ts): one-shot Anthropic structured output (messages.parse + json_schema) — not a tool loop. Grounds the model's account/envelope suggestions in real targets fetched from hledger; returns normalized proposals + dropdown context + any rows it couldn't parse. Never touches the journal.
  • Commit (commit.post.ts): the only write path. Re-validates each row server-side, dedups against the journal, and writes via appendSimplifiedTransaction — extracted from transactions.post.ts so manual-add and import share one envelope-accounting path. Partial success ({ committed, skippedDuplicates, failed }).
  • UI: editable staging grid (ImportReviewTable.vue), /import page with a persistent data-egress notice, no-API-key empty state, and a sidebar nav entry.

Notable design decisions

  • Structured outputs over a tool loop — purpose-built for one-shot CSV→JSON, compatible with adaptive thinking, no forced tool_choice.
  • Dedup is a flag, not a hard block — hashes date|cents|payee against the journal itself (no separate import ledger → stateless). Genuinely-identical same-day rows are surfaced as "possible duplicate" for the user to confirm rather than silently dropped; the commit-time journal check is what prevents accidental re-import of a committed batch.
  • Uncategorized handling — outflows require an envelope before approval; uncategorized inflows commit as income → Ready to Assign.
  • Row cap MAX_IMPORT_ROWS=200, non-streaming (streaming/chunking deferred).

Data egress (Issue #9 risk note)

CSV rows go to the Anthropic API — the one external data flow. No bank credentials, no aggregator, no stored secrets beyond ANTHROPIC_API_KEY. The import page shows a persistent notice; CSV contents and the key are never logged.

Spec

Design-first under .kiro/specs/ai-csv-import/ (design → requirements → tasks). Two small deviations recorded in the spec: the model echoes the verbatim sourceRow for reliable provenance; commit needs no API key (local write) — original R7.2 said "parse/commit".

Verification

  • npx vitest run58 files pass (39 new tests, incl. the load-bearing "parse writes 0×" safety test and refactor-parity on existing routes).
  • npx nuxi typecheck → clean (exit 0).
  • npm run build → succeeds.
  • No vitest.config / tsconfig / nuxt.config / package.json changes.
  • Not run: the live AI round-trip (upload → real Anthropic parse → commit) — needs a configured ANTHROPIC_API_KEY and a live API call. Everything verifiable statically is green; the end-to-end UI run is left to confirm with a key configured.

🤖 Generated with Claude Code

Upload a bank/credit-card CSV and have the AI map an arbitrary layout to
normalized transactions, reviewed and approved per-row before anything is
written. Builds on the #8 AI plumbing (shared Anthropic client + key config).

- parse.post.ts: one-shot structured output (messages.parse + json_schema),
  grounds suggestions in real accounts/envelopes, never writes.
- commit.post.ts: the only write path; re-validates, journal-based dedup
  (flag/skip, never silent drop), partial success, writes via the shared
  appendSimplifiedTransaction extracted from transactions.post.ts.
- useImport composable, ImportReviewTable, /import page with persistent
  data-egress notice + no-key empty state; sidebar nav entry.
- 39 new tests (incl. parse-never-writes safety); vitest, typecheck, build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: AI-assisted CSV transaction import (human-in-the-loop)

1 participant